#include "String.h"


namespace TL {
    String::String(int capacity, int size, char *text) : m_Undefined('\0')
    {
        m_Capacity = capacity;
        m_Size = size;

        m_Text = text;
    }

    short int String::strcmp(const char *str1, const char *str2)
    {
        int i;

        for (i = 0; str1[i] && str2[i]; i++) {
            if (str1[i] < str2[i]) {
                return -1;
            }
            else if (str1[i] > str2[i]) {
                return 1;
            }
        }

        if (!str1[i] && str2[i]) {
            return -1;
        }
        else if (str1[i] && !str2[i]) {
            return 1;
        }
        else {
            return 0;
        }
    }
    void String::strcat(char *dest, const char *src)
    {
        int i = 0;

        while (dest[i]) {
            i++;
        }

        for (int j = 0; src[j]; i++, j++) {
            dest[i] = src[j];
        }

        dest[i] = '\0';
    }
    void String::strcpy(char *dest, const char *src)
    {
        int i;

        for (i = 0; src[i]; i++)
            dest[i] = src[i];

        dest[i] = '\0';
    }
    int String::strlen(const char *str)
    {
        int i = 0;

        while (str[i]) {
            i++;
        }

        return i;
    }

    String::String() : m_Undefined('\0')
    {
        m_Capacity = 0;
        m_Size = 0;

        m_Text = new char[m_Capacity + 1];
        strcpy(m_Text, "");
    }
    String::String(int capacity) : m_Undefined('\0')
    {
        m_Capacity = capacity;
        m_Size = 0;

        m_Text = new char[m_Capacity + 1];
        strcpy(m_Text, "");
    }
    String::String(const char *str) : m_Undefined('\0')
    {
        m_Capacity = strlen(str);
        m_Size = m_Capacity;

        m_Text = new char[m_Capacity + 1];
        strcpy(m_Text, str);
    }
    String::String(const String &str) : m_Undefined('\0')
    {
        m_Capacity = str.m_Capacity;
        m_Size = str.m_Size;

        m_Text = new char[m_Capacity + 1];
        strcpy(m_Text, str.m_Text);
    }
    String::~String()
    {
        delete[] m_Text;
    }

    char &String::At(int index)
    {
        if (index < 0 || index >= m_Size) {
            return m_Undefined;
        }

        return m_Text[index];
    }
    const char &String::At(int index) const
    {
        if (index < 0 || index >= m_Size) {
            return m_Undefined;
        }

        return m_Text[index];
    }
    int String::Capacity() const
    {
        return m_Capacity;
    }
    int String::FindFirstSubstring(const char *substr, int offset)
    {
        if (offset < 0) {
            return -1;
        }
        
        int i = offset, j = 0;
        bool in_substr = false;

        // i - по основной строке, j - по подстроке
        while (i < m_Size) {
            if (!in_substr && m_Text[i] == substr[j]) { // вхождение в подстроку
                in_substr = true;

                i++;
                j++;
            }
            else if (in_substr && m_Text[i] == substr[j]) { // движение по подстроке
                i++;
                j++;
            }
            else if (in_substr && substr[j] == '\0') { // конец подстроки
                return (i - j);
            }
            else if (in_substr && m_Text[i] != substr[j]) { // выход из подстроки, несоответствие
                in_substr = false;

                i = i - j + 1;
                j = 0;
            }
            else if (!in_substr && m_Text[i] != substr[j]) { // поиск подстроки
                i++;
            }
        }

        // дополнительная проверка на случай, если подстрока находится аккурат в конце строки
        if (in_substr && substr[j] == '\0') {
            return (i - j);
        }

        return -1;
    }
    int String::FindFirstSubstring(const String &substr, int offset)
    {
        if (offset < 0) {
            return -1;
        }
        
        int i = offset, j = 0;
        bool in_substr = false;

        while (i < m_Size) {
            if (!in_substr && m_Text[i] == substr.m_Text[j]) {
                in_substr = true;

                i++;
                j++;
            }
            else if (in_substr && m_Text[i] == substr.m_Text[j]) {
                i++;
                j++;
            }
            else if (in_substr && substr.m_Text[j] == '\0') {
                return (i - j);
            }
            else if (in_substr && m_Text[i] != substr.m_Text[j]) {
                in_substr = false;

                i = i - j + 1;
                j = 0;
            }
            else if (!in_substr && m_Text[i] != substr.m_Text[j]) {
                i++;
            }
        }

        if (in_substr && substr.m_Text[j] == '\0') {
            return (i - j);
        }

        return -1;
    }
    int String::FindFirstSubstringEnd(const char *substr, int offset)
    {
        if (offset < 0) {
            return -1;
        }
        
        int i = offset, j = 0;
        bool in_substr = false;

        while (i < m_Size) {
            if (!in_substr && m_Text[i] == substr[j]) {
                in_substr = true;

                i++;
                j++;
            }
            else if (in_substr && m_Text[i] == substr[j]) {
                i++;
                j++;
            }
            else if (in_substr && substr[j] == '\0') {
                return (i - 1);
            }
            else if (in_substr && m_Text[i] != substr[j]) {
                in_substr = false;

                i = i - j + 1;
                j = 0;
            }
            else if (!in_substr && m_Text[i] != substr[j]) {
                i++;
            }
        }

        if (in_substr && substr[j] == '\0') {
            return (i - 1);
        }

        return -1;
    }
    int String::FindFirstSubstringEnd(const String &substr, int offset)
    {
        if (offset < 0) {
            return -1;
        }
        
        int i = offset, j = 0;
        bool in_substr = false;

        while (i < m_Size) {
            if (!in_substr && m_Text[i] == substr.m_Text[j]) {
                in_substr = true;

                i++;
                j++;
            }
            else if (in_substr && m_Text[i] == substr.m_Text[j]) {
                i++;
                j++;
            }
            else if (in_substr && substr.m_Text[j] == '\0') {
                return (i - 1);
            }
            else if (in_substr && m_Text[i] != substr.m_Text[j]) {
                in_substr = false;

                i = i - j + 1;
                j = 0;
            }
            else if (!in_substr && m_Text[i] != substr.m_Text[j]) {
                i++;
            }
        }

        if (in_substr && substr.m_Text[j] == '\0') {
            return (i - 1);
        }

        return -1;
    }
    const char *String::GetCharString() const
    {
        return m_Text;
    }
    String String::GetSubstring(int length, int offset)
    {
        if (offset < 0 || length < 1) {
            return String();
        }

        if (offset + length > m_Size) {
            length = m_Size - offset;
        }

        char *substr = new char[length + 1];

        for (int i = 0; i < length; i++) {
            substr[i] = m_Text[i + offset];
        }

        substr[length] = '\0';

        return String(length, length, substr);
    }
    void String::Insert(const char *str, int offset)
    {
        int str_length = strlen(str);

        if (offset < 0 || offset > m_Size || str_length == 0) {
            return;
        }

        String temp_str(m_Text);

        if (m_Size + str_length > m_Capacity) {
            m_Capacity = m_Size + str_length;

            delete[] m_Text;

            m_Text = new char[m_Capacity + 1];
        }

        for (int i = 0; i < offset; i++) {
            m_Text[i] = temp_str.m_Text[i];
        }

        for (int i = 0; i < str_length; i++) {
            m_Text[i + offset] = str[i];
        }

        for (int i = offset; i < m_Size; i++) {
            m_Text[i + str_length] = temp_str.m_Text[i];
        }

        m_Text[m_Size + str_length] = '\0';

        m_Size += str_length;
    }
    void String::Insert(const String &str, int offset)
    {
        if (offset < 0 || offset > m_Size || str.m_Size == 0) {
            return;
        }

        String temp_str(m_Text);

        if (m_Size + str.m_Size > m_Capacity) {
            m_Capacity = m_Size + str.m_Size;

            delete[] m_Text;

            m_Text = new char[m_Capacity + 1];
        }

        for (int i = 0; i < offset; i++) {
            m_Text[i] = temp_str.m_Text[i];
        }

        for (int i = 0; i < str.m_Size; i++) {
            m_Text[i + offset] = str.m_Text[i];
        }

        for (int i = offset; i < m_Size; i++) {
            m_Text[i + str.m_Size] = temp_str.m_Text[i];
        }

        m_Text[m_Size + str.m_Size] = '\0';

        m_Size += str.m_Size;
    }
    bool String::IsEmpty() const
    {
        return (m_Size == 0);
    }
    int String::Remove(const char *substr, int offset)
    {
        int str_length = strlen(substr);
        
        if (offset < 0 || str_length == 0) {
            return 0;
        }

        int remove_times = 0;

        char *new_str = new char[m_Capacity + 1];
        int i = offset, j = 0, k = 0;
        bool in_substr = false;

        while (i < m_Size) {
            if (!in_substr && m_Text[i] == substr[j]) {
                in_substr = true;

                i++;
                j++;
            }
            else if (in_substr && m_Text[i] == substr[j]) {
                i++;
                j++;
            }
            else if (in_substr && substr[j] == '\0') {
                remove_times++;

                in_substr = false;

                j = 0;
            }
            else if (in_substr && m_Text[i] != substr[j]) {
                new_str[k] = m_Text[i - j];

                in_substr = false;

                i = i - j + 1;
                j = 0;
                k++;
            }
            else if (!in_substr && m_Text[i] != substr[j]) {
                new_str[k] = m_Text[i];

                i++;
                k++;
            }
        }

        if (in_substr && substr[j] == '\0') {
            remove_times++;
        }

        new_str[k] = '\0';

        delete[] m_Text;
        m_Text = new_str;

        m_Size -= remove_times * str_length;

        return remove_times;
    }
    int String::Remove(const String &substr, int offset)
    {
        if (offset < 0 || substr.m_Size == 0) {
            return 0;
        }

        int remove_times = 0;

        char *new_str = new char[m_Capacity + 1];
        int i = offset, j = 0, k = 0;
        bool in_substr = false;

        while (i < m_Size) {
            if (!in_substr && m_Text[i] == substr.m_Text[j]) {
                in_substr = true;

                i++;
                j++;
            }
            else if (in_substr && m_Text[i] == substr.m_Text[j]) {
                i++;
                j++;
            }
            else if (in_substr && substr.m_Text[j] == '\0') {
                remove_times++;

                in_substr = false;

                j = 0;
            }
            else if (in_substr && m_Text[i] != substr.m_Text[j]) {
                new_str[k] = m_Text[i - j];

                in_substr = false;

                i = i - j + 1;
                j = 0;
                k++;
            }
            else if (!in_substr && m_Text[i] != substr.m_Text[j]) {
                new_str[k] = m_Text[i];

                i++;
                k++;
            }
        }

        if (in_substr && substr.m_Text[j] == '\0') {
            remove_times++;
        }

        new_str[k] = '\0';

        delete[] m_Text;
        m_Text = new_str;

        m_Size -= remove_times * substr.m_Size;

        return remove_times;
    }
    void String::RemoveSome(const char *parts, int offset)
    {
        int parts_length = strlen(parts);
        
        if (offset < 0 || parts_length == 0) {
            return;
        }

        char *new_str = new char[m_Capacity + 1];
        int i = offset, j, k = 0;
        bool match;

        while (i < m_Size) {
            for (j = 0, match = false; j < parts_length; j++) {
                if (m_Text[i] == parts[j]) {
                    match = true;
                    break;
                }
            }

            if (!match) {
                new_str[k++] = m_Text[i];
            }

            i++;
        }

        new_str[k] = '\0';

        delete[] m_Text;

        m_Size = k;
        m_Text = new_str;
    }
    void String::RemoveSome(const String &parts, int offset)
    {
        if (offset < 0 || parts.m_Size == 0) {
            return;
        }

        char *new_str = new char[m_Capacity + 1];
        int i = offset, j, k = 0;
        bool match;

        while (i < m_Size) {
            for (j = 0, match = false; j < parts.m_Size; j++) {
                if (m_Text[i] == parts.m_Text[j]) {
                    match = true;
                    break;
                }
            }

            if (!match) {
                new_str[k++] = m_Text[i];
            }

            i++;
        }

        new_str[k] = '\0';

        delete[] m_Text;

        m_Size = k;
        m_Text = new_str;
    }
    void String::RemoveSomeExcept(const char *exceptions, int offset)
    {
        int exceptions_length = strlen(exceptions);

        if (offset < 0 || exceptions_length == 0) {
            return;
        }

        char *new_str = new char[m_Capacity + 1];
        int i = offset, j, k = 0;
        bool match;

        while (i < m_Size) {
            for (j = 0, match = false; j < exceptions_length; j++) {
                if (m_Text[i] == exceptions[j]) {
                    match = true;
                    break;
                }
            }

            if (match) {
                new_str[k++] = m_Text[i];
            }

            i++;
        }

        new_str[k] = '\0';

        delete[] m_Text;

        m_Size = k;
        m_Text = new_str;
    }
    void String::RemoveSomeExcept(const String &exceptions, int offset)
    {
        if (offset < 0 || exceptions.m_Size == 0) {
            return;
        }

        char *new_str = new char[m_Capacity + 1];
        int i = offset, j, k = 0;
        bool match;

        while (i < m_Size) {
            for (j = 0, match = false; j < exceptions.m_Size; j++) {
                if (m_Text[i] == exceptions.m_Text[j]) {
                    match = true;
                    break;
                }
            }

            if (match) {
                new_str[k++] = m_Text[i];
            }

            i++;
        }

        new_str[k] = '\0';

        delete[] m_Text;

        m_Size = k;
        m_Text = new_str;
    }
    int String::Replace(const char *target_text, const char *replace_text, int offset)
    {
        int target_text_size = strlen(target_text);
        int replace_text_size = strlen(replace_text);

        if (offset < 0 || target_text_size == 0) {
            return 0;
        }

        int replace_times = 0;

        String new_str(m_Capacity);
        char temp_str[2] = "?";
        int i = offset, j = 0;
        bool in_substr = false;

        while (i < m_Size) {
            if (!in_substr && m_Text[i] == target_text[j]) {
                in_substr = true;

                i++;
                j++;
            }
            else if (in_substr && m_Text[i] == target_text[j]) {
                i++;
                j++;
            }
            else if (in_substr && target_text[j] == '\0') {
                replace_times++;
                new_str += replace_text;

                in_substr = false;

                j = 0;
            }
            else if (in_substr && m_Text[i] != target_text[j]) {
                temp_str[0] = m_Text[i - j];
                new_str += temp_str;

                in_substr = false;

                i = i - j + 1;
                j = 0;
            }
            else if (!in_substr && m_Text[i] != target_text[j]) {
                temp_str[0] = m_Text[i];
                new_str += temp_str;

                i++;
            }
        }

        if (in_substr && target_text[j] == '\0') {
            replace_times++;
            new_str += replace_text;
        }

        if (m_Capacity < new_str.m_Size) {
            m_Capacity = new_str.m_Size;

            delete[] m_Text;

            m_Text = new char[m_Capacity + 1];
        }

        strcpy(m_Text, new_str.m_Text);

        m_Size += replace_times * (replace_text_size - target_text_size);

        return replace_times;
    }
    int String::Replace(const char *target_text, const String &replace_text, int offset)
    {
        int target_text_size = strlen(target_text);

        if (offset < 0 || target_text_size == 0) {
            return 0;
        }

        int replace_times = 0;

        String new_str(m_Capacity);
        char temp_str[2] = "?";
        int i = offset, j = 0;
        bool in_substr = false;

        while (i < m_Size) {
            if (!in_substr && m_Text[i] == target_text[j]) {
                in_substr = true;

                i++;
                j++;
            }
            else if (in_substr && m_Text[i] == target_text[j]) {
                i++;
                j++;
            }
            else if (in_substr && target_text[j] == '\0') {
                replace_times++;
                new_str += replace_text;

                in_substr = false;

                j = 0;
            }
            else if (in_substr && m_Text[i] != target_text[j]) {
                temp_str[0] = m_Text[i - j];
                new_str += temp_str;

                in_substr = false;

                i = i - j + 1;
                j = 0;
            }
            else if (!in_substr && m_Text[i] != target_text[j]) {
                temp_str[0] = m_Text[i];
                new_str += temp_str;

                i++;
            }
        }

        if (in_substr && target_text[j] == '\0') {
            replace_times++;
            new_str += replace_text;
        }

        if (m_Capacity < new_str.m_Size) {
            m_Capacity = new_str.m_Size;

            delete[] m_Text;

            m_Text = new char[m_Capacity + 1];
        }

        strcpy(m_Text, new_str.m_Text);

        m_Size += replace_times * (replace_text.m_Size - target_text_size);

        return replace_times;
    }
    int String::Replace(const String &target_text, const char *replace_text, int offset)
    {
        int replace_text_size = strlen(replace_text);

        if (offset < 0 || target_text.m_Size == 0) {
            return 0;
        }

        int replace_times = 0;

        String new_str(m_Capacity);
        char temp_str[2] = "?";
        int i = offset, j = 0;
        bool in_substr = false;

        while (i < m_Size) {
            if (!in_substr && m_Text[i] == target_text.m_Text[j]) {
                in_substr = true;

                i++;
                j++;
            }
            else if (in_substr && m_Text[i] == target_text.m_Text[j]) {
                i++;
                j++;
            }
            else if (in_substr && target_text.m_Text[j] == '\0') {
                replace_times++;
                new_str += replace_text;

                in_substr = false;

                j = 0;
            }
            else if (in_substr && m_Text[i] != target_text.m_Text[j]) {
                temp_str[0] = m_Text[i - j];
                new_str += temp_str;

                in_substr = false;

                i = i - j + 1;
                j = 0;
            }
            else if (!in_substr && m_Text[i] != target_text.m_Text[j]) {
                temp_str[0] = m_Text[i];
                new_str += temp_str;

                i++;
            }
        }

        if (in_substr && target_text.m_Text[j] == '\0') {
            replace_times++;
            new_str += replace_text;
        }

        if (m_Capacity < new_str.m_Size) {
            m_Capacity = new_str.m_Size;

            delete[] m_Text;

            m_Text = new char[m_Capacity + 1];
        }

        strcpy(m_Text, new_str.m_Text);

        m_Size += replace_times * (replace_text_size - target_text.m_Size);

        return replace_times;
    }
    int String::Replace(const String &target_text, const String &replace_text, int offset)
    {
        if (offset < 0 || target_text.m_Size == 0) {
            return 0;
        }

        int replace_times = 0;

        String new_str(m_Capacity);
        char temp_str[2] = "?";
        int i = offset, j = 0;
        bool in_substr = false;

        while (i < m_Size) {
            if (!in_substr && m_Text[i] == target_text.m_Text[j]) {
                in_substr = true;

                i++;
                j++;
            }
            else if (in_substr && m_Text[i] == target_text.m_Text[j]) {
                i++;
                j++;
            }
            else if (in_substr && target_text.m_Text[j] == '\0') {
                replace_times++;
                new_str += replace_text;

                in_substr = false;

                j = 0;
            }
            else if (in_substr && m_Text[i] != target_text.m_Text[j]) {
                temp_str[0] = m_Text[i - j];
                new_str += temp_str;

                in_substr = false;

                i = i - j + 1;
                j = 0;
            }
            else if (!in_substr && m_Text[i] != target_text.m_Text[j]) {
                temp_str[0] = m_Text[i];
                new_str += temp_str;

                i++;
            }
        }

        if (in_substr && target_text.m_Text[j] == '\0') {
            replace_times++;
            new_str += replace_text;
        }

        if (m_Capacity < new_str.m_Size) {
            m_Capacity = new_str.m_Size;

            delete[] m_Text;

            m_Text = new char[m_Capacity + 1];
        }

        strcpy(m_Text, new_str.m_Text);

        m_Size += replace_times * (replace_text.m_Size - target_text.m_Size);

        return replace_times;
    }
    int String::Size() const
    {
        return m_Size;
    }

    bool operator==(const char *str1, const String &str2)
    {
        if (String::strlen(str1) == str2.m_Size) {
            return (String::strcmp(str1, str2.m_Text) == 0);
        }

        return false;
    }
    bool operator==(const String &str1, const char *str2)
    {
        if (str1.m_Size == String::strlen(str2)) {
            return (String::strcmp(str1.m_Text, str2) == 0);
        }

        return false;
    }
    bool operator==(const String &str1, const String &str2)
    {
        if (str1.m_Size == str2.m_Size) {
            return (String::strcmp(str1.m_Text, str2.m_Text) == 0);
        }

        return false;
    }
    bool operator!=(const char *str1, const String &str2)
    {
        if (String::strlen(str1) == str2.m_Size) {
            return (String::strcmp(str1, str2.m_Text) != 0);
        }

        return true;
    }
    bool operator!=(const String &str1, const char *str2)
    {
        if (str1.m_Size == String::strlen(str2)) {
            return (String::strcmp(str1.m_Text, str2) != 0);
        }

        return true;
    }
    bool operator!=(const String &str1, const String &str2)
    {
        if (str1.m_Size == str2.m_Size) {
            return (String::strcmp(str1.m_Text, str2.m_Text) != 0);
        }

        return true;
    }
    String &String::operator=(const char *str)
    {
        int str_length = strlen(str);

        if (str_length > m_Capacity) {
            m_Capacity = str_length;

            delete[] m_Text;

            m_Text = new char[m_Capacity + 1];
        }

        strcpy(m_Text, str);

        m_Size = str_length;

        return *this;
    }
    String &String::operator=(const String &str)
    {
        if (str.m_Size > m_Capacity) {
            m_Capacity = str.m_Capacity;

            delete[] m_Text;

            m_Text = new char[m_Capacity + 1];
        }

        strcpy(m_Text, str.m_Text);

        m_Size = str.m_Size;

        return *this;
    }
    String operator+(const char *str1, const String &str2)
    {
        int length = String::strlen(str1) + str2.m_Size;
        char *str = new char[length + 1];

        String::strcpy(str, str1);
        String::strcat(str, str2.m_Text);

        return String(length, length, str);
    }
    String operator+(const String &str1, const char *str2)
    {
        int length = str1.m_Size + String::strlen(str2);
        char *str = new char[length + 1];

        String::strcpy(str, str1.m_Text);
        String::strcat(str, str2);

        return String(length, length, str);
    }
    String operator+(const String &str1, const String &str2)
    {
        int length = str1.m_Size + str2.m_Size;
        char *str = new char[length + 1];

        String::strcpy(str, str1.m_Text);
        String::strcat(str, str2.m_Text);

        return String(length, length, str);
    }
    String &String::operator+=(const char *str)
    {
        int length = strlen(str) + m_Size;

        if (length > m_Capacity) {
            char *old_text = m_Text;

            m_Capacity = length;

            m_Text = new char[m_Capacity + 1];

            strcpy(m_Text, old_text);
        }

        strcat(m_Text, str);

        m_Size = length;

        return *this;
    }
    String &String::operator+=(const String &str)
    {
        int length = str.m_Size + m_Size;

        if (length > m_Capacity) {
            char *old_text = m_Text;

            m_Capacity = length;

            m_Text = new char[m_Capacity + 1];

            strcpy(m_Text, old_text);
        }

        strcat(m_Text, str.m_Text);

        m_Size = length;

        return *this;
    }
    char &String::operator[](int index)
    {
        return m_Text[index];
    }
    const char &String::operator[](int index) const
    {
        return m_Text[index];
    }
}
